package com.myCode.thread;

import java.sql.Time;
import java.util.concurrent.*;

/**
 * 线程池
 * 问题？线程是不是越多越好？
 * 1、线程在java中是一个对象，更是操作系统的资源，线程创建、销毁需要时间。如果创建时间+销毁时间 > 执行任务时间 就很不合算
 * 2、java对象占用堆内存，操作系统线程占用系统内存，根据 JVM 规范，一个线程默认最大栈大小1M，这个栈空间是需要从系统内存中分配。线程过多，会消耗很多内存
 * 3、操作系统需要频繁切换线程上下文，大家都想被运行，影响性能
 * 所以，线程池的推出，就是为了方便的控制线程数量
 *
 * 线程池原理 -- 概念
 * 1、线程池管理器：用于创建并管理线程池，包括创建线程池，销毁线程池，添加新任务
 * 2、工作线程：线程池中线程，在没有任务时处于等待状态，可以循环的执行任务
 * 3、任务接口：每个任务必须实现的接口，以供工作线程调度任务的执行，它主要规定了任务的入口，任务执行完后的收尾工作，任务的执行状态等
 * 4、任务队列：用于存放没有处理的任务。提供一种缓冲机制。
 *
 * 线程数量？
 * 计算型线程：cpu数量的1-2倍
 * IO型任务：相比与计算型任务，需要多一些线程，要根据具体的IO阻塞时长进行考量决定，如：Tomcat 中默认的最大线程数为 200
 * 也可以考虑根据需要在一个最小数量和最大数量间自动增减线程数
 */
public class ThreadPool {

    /**
     * 线程池工具类
     */
    private Executors executors;

    public void ExecutorsTest(){
        // 创建一个固定大小、任务队列容量无界的线程池。核心线程数=最大线程数
        Executors.newFixedThreadPool(10);
        // 创建一个大小无界的缓冲线程池，它的任务队列是一个同步队列。任务加入池中，如果池中有空闲线程，则用空闲线程执行，如无则创建新线程执行。
        // 池中的线控空闲超过60秒，将被销毁释放。线程数随任务的多少变化。
        // 适用于执行耗时较小的异步任务。池的核心线程数=0，最大线程数= Integer.MAX_VALUE
        Executors.newCachedThreadPool();
        // 只有一个线程来执行无界任务对接的单一线程池。该线程池确保任务按加入的顺序一个一个依次执行
        // 当唯一的线程因任务异常中止时，将创建一个新的线程来继续执行后续的任务
        // 与newFixedThreadPool(1) 的区别在于，单一线程池的池大小在 newSingleThreadExecutor 方法中硬编码，不能再改变的
        Executors.newSingleThreadExecutor();
        // 能定时执行任务的线程池。该池的核心线程数由参数指定，最大线程数= Integer.MAX_VALUE
        Executors.newScheduledThreadPool(10);
    }

    /**
     * 最上层的接口，定义了执行任务的方法 execute
     */
    Executor executor;
    /**
     * 继承了Executor接口，扩展了 Callable、Future、关闭方法
     */
    ExecutorService executorService;
    /**
     *
     * 继承了 ExecutorService，增加了定时任务相关的方法
     */
    ScheduledExecutorService scheduledExecutorService;
    /**
     * 基础、标准的线程池实现
     */
    ThreadPoolExecutor threadPoolExecutor;
    /**
     * 继承了ThreadPoolExecutor，实现了 ScheduledExecutorService中相关定时任务的方法
     */
    ScheduledThreadPoolExecutor scheduledThreadPoolExecutor;

    public void scheduledAPIDetails(){
        /**
         * 创建并执行一个一次性任务，过了延迟时间就会被执行
         */
        scheduledThreadPoolExecutor.schedule(new Callable<Object>() {
            @Override
            public Object call() throws Exception {
                return null;
            }
        }, 100, TimeUnit.SECONDS);
        scheduledThreadPoolExecutor.schedule(()->{}, 100, TimeUnit.SECONDS);

        /**
         * 创建并执行一个周期性任务
         * 过了给定的初试延迟时间，会第一次被执行，执行过程中发生异常，那么任务就停止
         * 如果某一次任务执行时间超过周期时间，下一次任务会等到该次任务结束后立即执行
         */
        scheduledThreadPoolExecutor.scheduleAtFixedRate(() ->{}, 100,100,TimeUnit.SECONDS);

        /**
         * 创建并执行一个周期性任务
         * 过了给定的初试延迟时间，会第一次被执行，执行过程中发生异常，那么任务就停止
         * 如果某一次任务执行时间超过周期时间，下一次任务会等到该次任务结束后的基础上，计算执行延时，不会立即执行下一次周期任务
         */
        scheduledThreadPoolExecutor.scheduleWithFixedDelay(()->{}, 100, 100, TimeUnit.SECONDS);
    }

    public void threadPoolExecutorTest() throws InterruptedException {
        /**
         * 线程池信息：
         *     核心线程数量(corePoolSize)：5
         *     最大数量(maximumPoolSize)：10
         *     临时线程存活时间(keepAliveTime)：5
         *     存活时间单位：TimeUnit.SECONDS 秒
         *     任务队列(BlockingQueue)：无界队列
         * 任务execute过程
         * 1、是否达到核心线程数量？没达到，创建一个工作线程来执行任务
         * 2、工作队列是否已满？没满，则将新提交的任务存储在工作队列中
         * 3、是否达到线程池最大数量？没达到，则创建一个新的工作线程来执行任务。
         * 4、最后，执行拒绝策略来处理添加的任务
         *
         * 由于下面这个线程池，我们使用的时 LinkedBlockingQueue 的无界队列 所以，工作队列不存在满的情况，线程池不会创建临时工作线程的
         */
        ThreadPoolExecutor linkedBlockingQueue = new ThreadPoolExecutor(5, 10,
                5, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());

        // testCommon(linkedBlockingQueue);

        /**
         * new LinkedBlockingQueue<Runnable>(3)：这里设定了工作队列只能缓存3个任务，超过后使用配置的拒绝策略：RejectedExecutionHandler，
         * 会调用 RejectedExecutionHandler 的 rejectedExecution 方法
         */
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 10, 5, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(3), new RejectedExecutionHandler() {
            @Override
            public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                System.err.println("有任务被拒绝执行了");
            }
        });

        /**
         * SynchronousQueue，实际上它不是一个真正的队列，因为它不会为队列中元素维护存储空间。与其他队列不同的是，它维护一组线程，这些线程在等待着把元素加入或移出队列
         * 使用 SynchronousQueue 作为工作队列的前提下，客户端代码向线程池提交任务时，而线程池中又没有空闲的线程能够从 SynchronousQueue 队列实例中取一个任务
         * 那么相应的 offer 方法调用就会失败（即任务没有被存入工作队列中）
         * 此时，ThreadPoolExecutor 会新建一个新的工作者线程用于堆这个入队列失败的任务进行处理（假如此时线程的大小还未达到最大线程池大小 maximumPoolSize）
         */
        ThreadPoolExecutor threadPoolExecutor1 = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L,
                TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
        // testCommon(threadPoolExecutor);

        /**
         * 核心线程数5，最大数量 Integer.MAX_VALUE，DelayedWorkQueue 延时队列，超出核心线程数量的线程存活时间：0秒
         */
        ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(5);
        // scheduledThreadPoolExecutor.schedule(() ->{
        //     System.out.println("任务被执行，现在时间：" + System.currentTimeMillis());
        // }, 3000, TimeUnit.MILLISECONDS);
        // System.out.println("定时任务，提交成功，时间是：" + System.currentTimeMillis() + ", 当前线程池中线程数量：" + scheduledThreadPoolExecutor.getPoolSize());

        /**
         * 周期性执行：
         * 提交的任务需要3秒才能执行完毕，看两种调度方式的不同
         */
        // scheduledThreadPoolExecutor.scheduleAtFixedRate(()->{
        //     try {
        //         Thread.sleep(3000L);
        //     } catch (InterruptedException e) {
        //         e.printStackTrace();
        //     }
        //     System.out.println("任务-1被执行，现在时间：" + System.currentTimeMillis());
        // }, 2000, 1000, TimeUnit.MILLISECONDS);

        scheduledThreadPoolExecutor.scheduleWithFixedDelay(()->{
            try {
                Thread.sleep(3000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("任务-1被执行，现在时间：" + System.currentTimeMillis());
        }, 2000, 1000, TimeUnit.MILLISECONDS);
        // ThreadPoolExecutor threadPoolExecutor1 ;
    }

    private void testCommon(ThreadPoolExecutor threadPoolExecutor) throws InterruptedException {

        for (int i = 0; i < 15; i++) {
            int n = i;
            threadPoolExecutor.submit(() ->{
                try {
                    System.out.println("开始执行第：" + n + "个任务");
                    Thread.sleep(3000L);
                    System.err.println("执行结束第：" + n + "个任务");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
            System.out.println("开始执行第：" + n + "个任务");
        }
        // 查看线程数量，查看队列等待数量
        Thread.sleep(500L);
        System.out.println("当前线程池线程数量为：" + threadPoolExecutor.getPoolSize());
        System.out.println("当前线程池等待的数量为：" + threadPoolExecutor.getQueue().size());
    //    等待15秒，查看线程数量和队列数量（理论上，会被超出核心线程数量的线程自送销毁）
        Thread.sleep(15000L);
        System.out.println("当前线程池线程数量为：" + threadPoolExecutor.getPoolSize());
        System.out.println("当前线程池等待的数量为：" + threadPoolExecutor.getQueue().size());
    }
}
